Put a badge indicating the number of events created on each agent node.

It is clickable, and colored depending on its status.

Akinori MUSHA лет %!s(int64=10): %!d(string=назад)
Родитель
Сommit
460ba68a95
2 измененных файлов с 112 добавлено и 1 удалено
  1. 30 0
      app/assets/stylesheets/diagram.css.scss
  2. 82 1
      app/helpers/dot_helper.rb

+ 30 - 0
app/assets/stylesheets/diagram.css.scss

@@ -0,0 +1,30 @@
1
+.agent-diagram {
2
+  position: relative;
3
+  z-index: auto;
4
+
5
+  svg.diagram {
6
+    position: absolute;
7
+    z-index: 1;
8
+  }
9
+
10
+  .overlay-container {
11
+    position: absolute;
12
+    top: 0;
13
+    left: 0;
14
+    z-index: auto;
15
+
16
+    .overlay {
17
+      position: relative;
18
+      z-index: auto;
19
+      width: 100%;
20
+      height: 100%;
21
+
22
+      .badge {
23
+        position: absolute;
24
+        display: none;
25
+        color: white !important;
26
+        z-index: 2;
27
+      }
28
+    }
29
+  }
30
+}

+ 82 - 1
app/helpers/dot_helper.rb

@@ -6,7 +6,7 @@ module DotHelper
6 6
           dot.close_write
7 7
           dot.read
8 8
         } rescue false)
9
-      svg.html_safe
9
+      decorate_svg(svg, agents).html_safe
10 10
     else
11 11
       tag('img', src: URI('https://chart.googleapis.com/chart').tap { |uri|
12 12
             uri.query = URI.encode_www_form(cht: 'gv', chl: agents_dot(agents))
@@ -161,4 +161,85 @@ module DotHelper
161 161
       }
162 162
     }
163 163
   end
164
+
165
+  def decorate_svg(xml, agents)
166
+    svg = Nokogiri::XML(xml).at('svg')
167
+
168
+    Nokogiri::HTML::Document.new.tap { |doc|
169
+      doc << root = Nokogiri::XML::Node.new('div', doc) { |div|
170
+        div['class'] = 'agent-diagram'
171
+      }
172
+
173
+      svg['class'] = 'diagram'
174
+
175
+      root << svg
176
+      root << overlay_container = Nokogiri::XML::Node.new('div', doc) { |div|
177
+        div['class'] = 'overlay-container'
178
+        div['style'] = "width: #{svg['width']}; height: #{svg['height']}"
179
+      }
180
+      overlay_container << overlay = Nokogiri::XML::Node.new('div', doc) { |div|
181
+        div['class'] = 'overlay'
182
+      }
183
+
184
+      svg.xpath('//xmlns:g[@class="node"]', svg.namespaces).each { |node|
185
+        agent_id = (node.xpath('./xmlns:title/text()', svg.namespaces).to_s[/\d+/] or next).to_i
186
+        agent = agents.find { |a| a.id == agent_id }
187
+
188
+        count = agent.events_count
189
+        next unless count && count > 0
190
+
191
+        overlay << Nokogiri::XML::Node.new('a', doc) { |badge|
192
+          badge['id'] = id = 'b%d' % agent_id
193
+          badge['class'] = 'badge'
194
+          badge['href'] = events_path(agent: agent)
195
+          badge['target'] = '_blank'
196
+          badge.content = count.to_s
197
+
198
+          node['data-badge-id'] = id
199
+
200
+          badge << Nokogiri::XML::Node.new('span', doc) { |label|
201
+            # a dummy label only to obtain the background color
202
+            label['class'] = [
203
+              'label',
204
+              if agent.disabled?
205
+                'label-warning'
206
+              elsif agent.working?
207
+                'label-success'
208
+              else
209
+                'label-danger'
210
+              end
211
+            ].join(' ')
212
+            label['style'] = 'display: none';
213
+          }
214
+        }
215
+      }
216
+
217
+      root << Nokogiri::XML::Node.new('script', doc) { |script|
218
+        script.content = <<-SCRIPT
219
+$(function () {
220
+  var svg = document.querySelector('.agent-diagram svg.diagram');
221
+  var overlay = document.querySelector('.agent-diagram .overlay');
222
+  var getTopLeft = function (node) {
223
+    var bbox = node.getBBox();
224
+    var point = svg.createSVGPoint();
225
+    point.x = bbox.x + bbox.width;
226
+    point.y = bbox.y;
227
+    return point.matrixTransform(node.getCTM());
228
+  };
229
+  $(svg).find('g.node[data-badge-id]').each(function () {
230
+    var tl = getTopLeft(this)
231
+    $('#' + this.getAttribute('data-badge-id'), overlay).each(function () {
232
+      var badge = $(this);
233
+      badge.css({
234
+        left: tl.x - badge.outerWidth()  * (2/3),
235
+        top:  tl.y - badge.outerHeight() * (1/3),
236
+        'background-color': badge.find('.label').css('background-color')
237
+      }).show();
238
+    });
239
+  });
240
+})
241
+        SCRIPT
242
+      }
243
+    }.at('div.agent-diagram').to_s
244
+  end
164 245
 end